package com.linkoog.devtools.type;

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.util.PsiUtil;
import com.linkoog.devtools.type.impl.ArrayType;
import com.linkoog.devtools.type.impl.FileType;
import com.linkoog.devtools.type.impl.LiteralTypes;
import com.linkoog.devtools.type.impl.MapType;
import com.linkoog.devtools.type.impl.ObjectFieldDoc;
import com.linkoog.devtools.type.impl.ObjectType;
import com.linkoog.devtools.type.impl.UnknownType;
import com.linkoog.devtools.type.javadoc.JavaDoc;
import com.linkoog.devtools.type.javadoc.JavaDocParser;
import com.linkoog.devtools.type.swagger.JavaSwaggerParser;
import com.linkoog.devtools.type.swagger.SwaggerFieldDoc;
import com.linkoog.devtools.utils.StringUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class PsiClassTypeParser {

    public static Type parse(PsiClass psiClass , Map<String,ObjectType> parentTypes) {
        if (psiClass == null) return null;

        //当前类,为简单的数据类型:
        // 基础类型和封装类型
        LiteralType literalType = LiteralTypes.valueByFullName(psiClass.getQualifiedName());
        if (literalType != null) return literalType;

        //1.number类型  2.string类型   3.date类型
        LiteralType derivativeLiteralType = TypeParserUtils.parseDerivativeLiteralType(psiClass);
        if (derivativeLiteralType != null) return derivativeLiteralType;

        //文件类型
        if (TypeParserUtils.isFileType(psiClass)){
            //psiType.getCanonicalText()  psiType.getPresentableText()
            return new FileType(psiClass.getName(), psiClass.getQualifiedName());
        }

        /*---------------------------对象类型解析-----------------------------------------------------*/

        //当前字段的实际泛型
        PsiTypeParameter[] fieldWildcardParamPsiTypes = psiClass.getTypeParameters();
        boolean hasWildcardParam = fieldWildcardParamPsiTypes.length != 0; //是否有实际的泛型参数

        //当前类, 为list类型
        if (TypeParserUtils.isCollectionType(psiClass)){
            Type dataType;
            if (hasWildcardParam){
                dataType = parse(fieldWildcardParamPsiTypes[0], parentTypes);
            } else {
                dataType = null;
            }

            //psiType.getPresentableText()  psiClass.getQualifiedName() psiType.getCanonicalText()
            return new ArrayType(psiClass.getName(), psiClass.getQualifiedName(), dataType);
        }

        //当前类, 为map类型
        if (TypeParserUtils.isMapType(psiClass)){
            Type keyType, valType;
            if (hasWildcardParam){
                keyType = parse(fieldWildcardParamPsiTypes[0], parentTypes);
                valType = parse(fieldWildcardParamPsiTypes[1], parentTypes);
            } else {
                keyType = null;
                valType = null;
            }

            return new MapType(keyType, valType);
        }

        //当前类, 为无泛型或带泛型的pojo实体类
        ObjectType objectType = new ObjectType(psiClass.getName(), psiClass.getQualifiedName(), psiClass.getQualifiedName(), psiClass.getName());


        Map<String, PsiTypeParameter> wildcardFieldMetaDataMap = new HashMap<>();
        Iterator<PsiTypeParameter> typeParameterIterator = PsiUtil.typeParametersIterator(psiClass); //遍历类的泛型声明
        for (int i= 0; typeParameterIterator.hasNext() ; i ++){
            PsiTypeParameter psiTypeParameter = typeParameterIterator.next();
            if (i < fieldWildcardParamPsiTypes.length){
                //当字段上设置了泛型时
                PsiTypeParameter wildcardPsiType = fieldWildcardParamPsiTypes[i];
                wildcardFieldMetaDataMap.put(psiTypeParameter.getName(), wildcardPsiType);
            } else {
                wildcardFieldMetaDataMap.put(psiTypeParameter.getName(), null);
            }
        }

        //当前类，解析所有字段
        Map<String, Type> fieldTypeMap = new HashMap<>();
        Map<String, ObjectFieldDoc> fieldDocMap = new HashMap<>();
        for (PsiField psiField : psiClass.getAllFields()) {
            Type fieldType = null;
            if (wildcardFieldMetaDataMap.containsKey(psiField.getType().getCanonicalText())){
                //处理泛型字段，wildcardPsiType 为泛型的实际类型
                PsiTypeParameter wildcardPsiType = wildcardFieldMetaDataMap.get(psiField.getType().getCanonicalText());
                if (wildcardPsiType == null){
                    //字段为泛型，但是无实际泛型
                    fieldType = new UnknownType();
                }else {
                    fieldType = parse(wildcardPsiType, parentTypes);
                }
            } else {
                //不是泛型的字段
                fieldType = TypeParser.parsePsiType(psiField.getType(), parentTypes);
            }

            ObjectFieldDoc objectFieldDoc = new ObjectFieldDoc();
            objectFieldDoc.setName(psiField.getName());

            //java 字段文档解析
            JavaDoc javaDoc = JavaDocParser.parse(psiField.getDocComment());
            if (javaDoc != null){
                objectFieldDoc.setDescription(javaDoc.getDescription());
            }

            //swagger文档解析
            SwaggerFieldDoc swaggerFieldDoc = JavaSwaggerParser.parseField(psiField);
            if (swaggerFieldDoc != null){
                objectFieldDoc.setRequired(swaggerFieldDoc.isRequired());
                objectFieldDoc.setExample(swaggerFieldDoc.getExample());
                if (StringUtils.isNotBlank(swaggerFieldDoc.getValue())){
                    objectFieldDoc.setDescription(swaggerFieldDoc.getValue());
                }
            }

            //验证注解
            objectFieldDoc.setRequired(TypeParserUtils.hasValidationAnno(psiField));

            fieldTypeMap.put(psiField.getName(), fieldType);
            fieldDocMap.put(psiField.getName(), objectFieldDoc);
        }

        objectType.setFieldTypeMap(fieldTypeMap);
        objectType.setFieldDocMap(fieldDocMap);

        //当前类,是否有泛型声明
            /*boolean hasTypeWildcard = !wildcardFieldMetaDataMap.isEmpty();
            if (hasTypeWildcard){

            }*/

        //当前类,为pojo实体类时，添加到set中，为下级字段是否为父类的判断，做数据准备
        parentTypes.put(psiClass.getQualifiedName(), objectType);
        return objectType;
    }


}
